﻿
//////////////////////////////////////////////////////////////////////////////
//
// Copyright © 1998-2024 Glodon Company Limited.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the “Software”),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
//////////////////////////////////////////////////////////////////////////////

#include "IPick.h"
#include "IPicker.h"
#include "ILine3d.h"
#include "Vector3d.h"
#include "Matrix4d.h"
#include "ICommand.h"
#include "IDocument.h"
#include "IInstance.h"
#include "ActionBase.h"
#include "ISelection.h"
#include "IModelView.h"
#include "IPickFilter.h"
#include "IPickTarget.h"
#include "IPickResult.h"
#include "IUiDocument.h"
#include "IHighlights.h"
#include "UiViewUtils.h"
#include "IPreHighlights.h"
#include "IPickEventArgs.h"
#include "IViewerContext.h"
#include "IGraphicsPoint.h"
#include "IPickCandidates.h"
#include "IPickCandidates.h"
#include "IGraphicsCurve3d.h"
#include "GcmpCommandState.h"
#include "IPickEventHandler.h"
#include "IPointPickContext.h"
#include "TransferOwnership.h"
#include "InteractivePicker.h"
#include "UiDocumentViewUtils.h"
#include "IPureGraphicsElement.h"
#include "IGraphicsElementShape.h"
#include "IGraphicsNodeReference.h"
#include "PickNodeReferenceOption.h"
#include "IPickNodeReferenceAction.h"

#include "CommandRegister.h"
#include "SampleUIUtils.h"
#include "EnableCompileWarning_The_LAST_IncludeInCpp.h" 

using namespace gcmp;
using namespace std;

// 以下代码是在线文档中引用的代码，仅供参考

namespace CodeSnipper
{

    class SamplePickFilter : public IPickFilter
    {

    public:
        SamplePickFilter(){ }

        virtual bool AllowElement(const gcmp::ElementId& elementId) const override
        {
            return true;
        }

        virtual bool AllowGraphicsNode(const gcmp::IGraphicsNodeReference& graphicsNodeReference) const override
        {
            return true;
        }

        virtual bool SetPickTargetOption(gcmp::IPickTarget* pPickTarget) override
        {
            return true;
        }
    };

    class SamplePickEventHandler : public gcmp::IPickEventHandler
    {

    public:
        virtual void On(gcmp::IPickEventArgs* pArgs) override
        {
            IDocument* pDocument = pArgs->GetDocument();

            // 将拾取候选项按照是否Instance分开放入容器
            std::vector<OwnerPtr<IPick>> candidatesInstances;
            std::vector<OwnerPtr<IPick>> candidatesOthers;

            ElementId linkDocumentElementId;
            OwnerPtr<IPickResult> opResult = pArgs->GetPickCandidates().Clone();
            std::vector<OwnerPtr<IPick>>& candidates = opResult->GetAllPicks();
            FOR_EACH(candidate, candidates)
            {
                const GraphicsNodeReferenceOwnerPtrVector & gNodeReferences =
                    candidate->GetAllGraphicsNodeReferences();
                FOR_EACH(gNodeReference, gNodeReferences)
                {
                    ElementId elementId = gNodeReference->GetElementId();
                    if (const IInstance* pInstance = quick_cast<IInstance>(pDocument->GetElement(elementId)))
                    {
                        candidatesInstances.emplace_back(candidate->Clone());
                    }
                    else
                    {
                        candidatesOthers.emplace_back(candidate->Clone());
                    }
                }
            }

            // 有Instance被选中，候选集一定会变化：将所有Insance候选项先加入拾取候选集
            if (!candidatesInstances.empty())
            {
                candidates.clear();

                FOR_EACH(candidate, candidatesInstances)
                {
                    opResult->AddPick(TransferOwnership(candidate));
                }
                FOR_EACH(candidate, candidatesOthers)
                {
                    opResult->AddPick(TransferOwnership(candidate));
                }

                pArgs->SetPickCandidates(*opResult);
            }
        }
    };

    class SamplePickAction : public gcmp::ActionBase
    {

    public:
        SamplePickAction() :
            m_isUserCancelled(false)
        {
        }

        virtual void InitAction(IUiView* pCurrentView) override
        {
            IUiDocument* pUiDocument = UiDocumentViewUtils::GetCurrentUiDocument();
            gcmp::IDocument* pDoc = pUiDocument->GetDbDocument();
            DBG_WARN_AND_RETURN_VOID_UNLESS(pDoc, L"pDoc为空",L"GDMP",L"2023-12-30");
        }

        void EnterPickRectangleState()
        {
        }

        void EnterPickPolygonState()
        {
        }

        virtual bool OnLButtonDown(IUiView* pCurrentView, const Vector3d& pos) override
        {
            IUiDocument* pUiDocument = UiDocumentViewUtils::GetCurrentUiDocument();
            gcmp::IDocument* pDocument = pUiDocument->GetDbDocument();

            // 取消预高亮
            IPreHighlights* pPreHighlights = IPreHighlights::Get();
            pPreHighlights->Clear();

            ISelection* pSelection = ISelection::Get();

            bool ctrlKeyPressed = IsKeyAndButtonPressed(VK_CONTROL);
            bool shiftKeyPressed = IsKeyAndButtonPressed(VK_SHIFT);

            // 获取拾取候选集
            IPickCandidates* pPickCandidates = IPickCandidates::Get();

            if (const OwnerPtr<IPick>& opPick = pPickCandidates->GetCurrentPick())
            {
                if (const IGraphicsNodeReference* pGraphicsNodeReference = opPick->GetFront())
                {
                    //1. Ctrl 总是加
                    if (ctrlKeyPressed && !shiftKeyPressed)
                    {
                        pSelection->AddGraphicsNodeReference(pDocument, *pGraphicsNodeReference);
                    }
                    //2. Shift总是减
                    else if (!ctrlKeyPressed && shiftKeyPressed)
                    {
                        pSelection->DeleteGraphicsNodeReference(pDocument, *pGraphicsNodeReference);
                    }
                    else
                    {
                        pSelection->Clear(pDocument);
                        pSelection->AddGraphicsNodeReference(pDocument, *pGraphicsNodeReference);
                    }

                    IHighlights* pHighlights = IHighlights::Get();
                    pHighlights->Clear();
                    pHighlights->AddGraphicsNodeReferences(OwnerPtrContainerUtil::TransformSetToVector(pSelection->GetGraphicsNodeReferences()));
                    
                }
            }

            UpdateView();
            return true;
        }

        virtual bool OnKeyUp(IUiView* pCurrentView, int nChar)
        {
            if (VK_ESCAPE == nChar)
            {
                MarkFinishStatus(ActionFinishStatus::Successful);
            }
            return ActionBase::OnKeyDown(pCurrentView, nChar);
        }

        virtual bool OnLButtonDoubleClick(IUiView* pCurrentView, const Vector3d& pos) override
        {
            return ActionBase::OnLButtonDoubleClick(pCurrentView, pos);
        }

        virtual bool OnMovePoint(IUiView* pCurrentView, const Vector3d& pos) override
        {
            IModelView* pModelView = pCurrentView->GetModelView();
            IDocument* pDocument = pModelView->GetDocument();

            int screenX;
            int screenY;

            // 把在屏幕上取的点转换成射线
            Vector3d startPt = UiViewUtility::ScreenPositionToWorldPosition(pCurrentView, screenX, screenY, 0);
            Vector3d endPt = UiViewUtility::ScreenPositionToWorldPosition(pCurrentView, screenX, screenY, 1);
            auto ray = ILine3d::Create(startPt, endPt);

            // 设置拾取上下文
            OwnerPtr<IPointPickContext> opPickContext = gcmp::IPointPickContext::Create();

            opPickContext->SetDocument(pDocument);
            opPickContext->SetViewerContext(pCurrentView->GetViewerContext()->Clone());
            opPickContext->SetRay(ray.get());

            // 点选拾取。如果只是做拾取计算，到这里就可以结束了。
            OwnerPtr<IPickResult> opPickResult = gcmp::IPicker::PickByPoint(opPickContext.get());
            if (!opPickResult)
                return false;

            // 将拾取候选项加入预选集和预高亮集
            IPickCandidates::Get()->Clear();
            IPreHighlights::Get()->Clear();

            FOR_EACH(itIPick, opPickResult->GetAllPicks())
            {
                IPickCandidates::Get()->AddPick(TransferOwnership(itIPick));
            }
            IPickCandidates::Get()->SetDefaultCurrentPickIndex(pDocument);

            if (IPickCandidates::Get()->GetCount() > 0)
            {
                IPreHighlights::Get()->AddGraphicsNodeReferences(IPickCandidates::Get()->GetCurrentPick()->GetAllGraphicsNodeReferences());
            }
            // 刷新视图
            UpdateView();
            return true;
        }

        virtual void ActionCancelled() override
        {
            CleanupTempGraphicsShape();
        }

        void OnMove(gcmp::IUiView* pCurrentView, const gcmp::Vector3d& pos)
        {
        }

        void EnterPickElementState()
        {
            IUiDocument* pUiDocument = UiDocumentViewUtils::GetCurrentUiDocument();
            gcmp::IDocument* pDoc = pUiDocument->GetDbDocument();

            bool singleSelectInput = true;
            SamplePickEventHandler* pPickEventHandler;

            // singleSelectInput设置为点拾取，m_pPickedReferencesOutput用于返回拾取图形节点
            PickNodeReferenceExchangeData pickNodeReferenceExchangeData(singleSelectInput, &m_pickedReferencesOutput);

            // 设置拾取鼠标移动中的回调函数OnMove，IPickEventHandler可以过滤和增删拾取结果（在预高亮前）
            PickNodeReferenceOption pickNodeReferenceOption;
            pickNodeReferenceOption.SetMoveCallback(IPickNodeReferenceAction::CreateMoveCallback(&SamplePickAction::OnMove, this));
            pickNodeReferenceOption.SetPostEventHandler(pPickEventHandler);

            // 设置自定义拾取过滤器，例如只拾取满足某种条件的图元
            OwnerPtr<SamplePickFilter> opPickFilter = NEW_AS_OWNER_PTR(SamplePickFilter);

            // 创建IPickNodeReferenceAction并在子Action中启动
            gcmp::OwnerPtr<IAction> opPickNodeReferenceAction = IPickNodeReferenceAction::Create(
                pickNodeReferenceExchangeData, TransferOwnership(opPickFilter), pickNodeReferenceOption);
            StartChildAction(TransferOwnership(opPickNodeReferenceAction));
        }

        virtual void OnChildActionFinished(IUiView* pCurrentView, const ActionOutput& childActionReturnParam) override
        {
            IUiDocument* pUIDocument = pCurrentView->GetUiDocument();
            IDocument* pDocument = pUIDocument->GetDbDocument();

            ISelection* pSelection = ISelection::Get();
            DBG_WARN_AND_RETURN_VOID_UNLESS(pSelection, L"pSelection为空",L"GDMP",L"2023-12-30");
            pSelection->Clear(pDocument);

            for (const OwnerPtr<IGraphicsNodeReference>& itor : m_pickedReferencesOutput)
            {
                pSelection->AddGraphicsNodeReference(pDocument, *itor.get());
            }

            if (m_isUserCancelled)
            {
                MarkFinishStatus(ActionFinishStatus::Successful);
            }
            else
            {
                EnterPickElementState();
            }
        }

    private:
        bool m_isUserCancelled;
        gcmp::Vector3d m_ptMousePoint;
        gcmp::GraphicsNodeReferenceOwnerPtrVector m_pickedReferencesOutput;
    };

    class PickerMultiSelectCmd : gcmp::ICommand
    {

        gcmp::OwnerPtr<gcmp::IAction> PickerMultiSelectCmd::ExecuteCommand(const gcmp::CommandParameters& cmdParams)
        {
            Vector3d point;
            GraphicsNodeReferenceOwnerPtrVector nodeReferences;
            InteractivePicker::Select(true, false, nullptr, L"多选", L"多选", point, nodeReferences, true, NEW_AS_OWNER_PTR(SamplePickEventHandler));

            // 后面是清除预高亮，将拾取结果高亮和放入选择集
            IPreHighlights* pPreHighlights = IPreHighlights::Get();
            pPreHighlights->Clear();

            IDocument* pDocument = UiDocumentViewUtils::GetCurrentDocument();

            ISelection* pSelection = ISelection::Get();
            DBG_WARN_AND_RETURN_FALSE_UNLESS(pSelection, L"pSelection为空",L"GDMP",L"2023-12-30");
            pSelection->Clear(pDocument);
            IHighlights* pHighlights = IHighlights::Get();
            pHighlights->Clear();

            IPickCandidates* pPickCandidates = IPickCandidates::Get();

            if (const OwnerPtr<IPick>& opPick = pPickCandidates->GetCurrentPick())
            {
                if (const IGraphicsNodeReference* pGraphicsNodeReference = opPick->GetFront())
                {
                    pSelection->AddGraphicsNodeReference(pDocument, *pGraphicsNodeReference);
                    pHighlights->AddGraphicsNodeReference(*pGraphicsNodeReference);
                }
            }

            IUiDocument* pUiDocument = UiDocumentViewUtils::GetCurrentUiDocument();
            pUiDocument->UpdateView();
            
            return nullptr;
        }

        void AddElementToSelection(IDocument* pDocument, ElementId id)
        {
            GraphicsNodeReferenceOwnerPtrSet selectedElements;
            OwnerPtr<IGraphicsNodeReference> opGNodeRef = IGraphicsNodeReference::CreateGraphicsElementShapeReference(id);
            OwnerPtrContainerUtil::AddItem(selectedElements, TransferOwnership(opGNodeRef));

            ISelection::Get()->AddGroupGraphicsNodeReference(pDocument, selectedElements);
            std::vector<OwnerPtr<IGraphicsNodeReference>> opGnodRefs;

            OwnerPtrContainerUtil::AddItems(opGnodRefs, selectedElements);
            IHighlights::Get()->AddGraphicsNodeReferences(opGnodRefs);
        }
    };
}

